﻿using FCSChart.Axis;
using FCSChart.Graphical;
using System;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Input;
using System.Windows.Media;

namespace FCSChart
{
    public class ChartWithGraphicals : Chart
    {
        static ChartWithGraphicals()
        {
            DefaultStyleKeyProperty.OverrideMetadata(typeof(ChartWithGraphicals), new FrameworkPropertyMetadata(typeof(ChartWithGraphicals)));
        }

        public ChartWithGraphicals()
        {
            this.Loaded += (sender, e) =>
            {
                if (Graphicals == null) Graphicals = new ObservableCollection<BaseGraphical>();
                if (AllAreas == null) AllAreas = new ObservableCollection<GraphicalArea>();
                DrawingGraphicals();
            };
            PolygonCommand = new DelegateCommand((e) => { Graphicals.Add(new PolygonGraphical()); });
            RectangleCommand = new DelegateCommand((e) => { Graphicals.Add(new RectangleGraphical()); });
            SegmentXCommand = new DelegateCommand((e) => { Graphicals.Add(new SegmentXGraphical()); });
            SegmentYCommand = new DelegateCommand((e) => { Graphicals.Add(new SegmentYGraphical()); });
            QuadrantCommand = new DelegateCommand((e) => { Graphicals.Add(new QuadrantGraphical()); });
            LineHorizontalCommand = new DelegateCommand((e) => { Graphicals.Add(new LineHorizontalGraphical()); });
            LineVerticalCommand = new DelegateCommand((e) => { Graphicals.Add(new LineVerticalGraphical()); });
        }

        /// <summary>
        /// 门区域命名方法
        /// </summary>
        public Func<BaseGraphical, string> CreateNewAreaNameFunction { internal get; set; } = Helper.CreateNewNameFaction;

        #region 门

        /// <summary>
        /// 是否可以删除门，用于门ContentMenu显示
        /// </summary>
        public bool CanRemoveGraphical
        {
            get { return (bool)GetValue(CanRemoveGraphicalProperty); }
            set { SetValue(CanRemoveGraphicalProperty, value); }
        }
        public static readonly DependencyProperty CanRemoveGraphicalProperty = DependencyProperty.Register("CanRemoveGraphical", typeof(bool), typeof(ChartWithGraphicals), new PropertyMetadata(true, (sender, e) =>
        {
            if (sender is ChartWithGraphicals chart && chart.Graphicals != null)
            {
                foreach (var graphical in chart.Graphicals) graphical.OnPropertyChanged("ContextMenu");
            }
        }));
        /// <summary>
        /// 是否可以编辑图形
        /// </summary>
        public bool CanChangeGraphical
        {
            get { return (bool)GetValue(CanChangeGraphicalProperty); }
            set { SetValue(CanChangeGraphicalProperty, value); }
        }
        public static readonly DependencyProperty CanChangeGraphicalProperty = DependencyProperty.Register("CanChangeGraphical", typeof(bool), typeof(ChartWithGraphicals), new PropertyMetadata(true));

        /// <summary>
        /// 门的集合
        /// </summary>
        public ObservableCollection<BaseGraphical> Graphicals
        {
            get { return (ObservableCollection<BaseGraphical>)GetValue(GraphicalsProperty); }
            set { SetValue(GraphicalsProperty, value); }
        }
        public static readonly DependencyProperty GraphicalsProperty = DependencyProperty.Register("Graphicals", typeof(ObservableCollection<BaseGraphical>), typeof(ChartWithGraphicals), new PropertyMetadata(GraphicalsChanged));
        private static void GraphicalsChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is ChartWithGraphicals chart)
            {
                if (e.OldValue != null && e.OldValue is ObservableCollection<BaseGraphical> oldValue)
                {
                    oldValue.CollectionChanged -= chart.Graphicals_CollectionChanged;
                    foreach (var graphical in oldValue) graphical.Dispose();
                }
                if (e.NewValue != null && e.NewValue is ObservableCollection<BaseGraphical> newValue)
                {
                    newValue.CollectionChanged += chart.Graphicals_CollectionChanged;
                    foreach (var graphical in newValue)
                    {
                        graphical.OwnerChart = chart;
                        graphical.OnPropertyChanged("ContextMenu");
                        graphical.Drawing();
                    }
                }
            }
        }
        /// <summary>
        /// 门的集合增减
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected void Graphicals_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            switch (e.Action)
            {
                case NotifyCollectionChangedAction.Add:
                case NotifyCollectionChangedAction.Replace:
                    if (e.NewItems != null)
                        foreach (var item in e.NewItems)
                            if (item is BaseGraphical graphical)
                            {
                                graphical.OwnerChart = this;
                                graphical.OnPropertyChanged("ContextMenu");
                                graphical.Drawing();
                            }
                    break;
                default:
                    break;
            }
        }

        /// <summary>
        /// 多边形绘制命令
        /// </summary>
        public ICommand PolygonCommand
        {
            get { return (ICommand)GetValue(PolygonCommandProperty); }
            private set { SetValue(PolygonCommandProperty, value); }
        }
        public static readonly DependencyProperty PolygonCommandProperty = DependencyProperty.Register("PolygonCommand", typeof(ICommand), typeof(ChartWithGraphicals), new PropertyMetadata(null));

        /// <summary>
        /// 四边边形绘制命令
        /// </summary>
        public ICommand RectangleCommand
        {
            get { return (ICommand)GetValue(RectangleCommandProperty); }
            private set { SetValue(RectangleCommandProperty, value); }
        }
        public static readonly DependencyProperty RectangleCommandProperty = DependencyProperty.Register("RectangleCommand", typeof(ICommand), typeof(ChartWithGraphicals), new PropertyMetadata(null));

        /// <summary>
        /// X轴段选门绘制命令
        /// </summary>
        public ICommand SegmentXCommand
        {
            get { return (ICommand)GetValue(SegmentXCommandProperty); }
            private set { SetValue(SegmentXCommandProperty, value); }
        }
        public static readonly DependencyProperty SegmentXCommandProperty = DependencyProperty.Register("SegmentXCommand", typeof(ICommand), typeof(ChartWithGraphicals), new PropertyMetadata(null));
        /// <summary>
        /// Y轴段选门绘制命令
        /// </summary>
        public ICommand SegmentYCommand
        {
            get { return (ICommand)GetValue(SegmentYCommandProperty); }
            private set { SetValue(SegmentYCommandProperty, value); }
        }
        public static readonly DependencyProperty SegmentYCommandProperty = DependencyProperty.Register("SegmentYCommand", typeof(ICommand), typeof(ChartWithGraphicals), new PropertyMetadata(null));

        /// <summary>
        /// 4象限门绘制命令
        /// </summary>
        public ICommand QuadrantCommand
        {
            get { return (ICommand)GetValue(QuadrantCommandProperty); }
            private set { SetValue(QuadrantCommandProperty, value); }
        }
        public static readonly DependencyProperty QuadrantCommandProperty = DependencyProperty.Register("QuadrantCommand", typeof(ICommand), typeof(ChartWithGraphicals), new PropertyMetadata(null));
        /// <summary>
        /// 横线门
        /// </summary>
        public ICommand LineHorizontalCommand
        {
            get { return (ICommand)GetValue(LineHorizontalCommandProperty); }
            private set { SetValue(LineHorizontalCommandProperty, value); }
        }
        public static readonly DependencyProperty LineHorizontalCommandProperty = DependencyProperty.Register("LineHorizontalCommand", typeof(ICommand), typeof(ChartWithGraphicals), new PropertyMetadata(null));
        /// <summary>
        /// 竖线门
        /// </summary>
        public ICommand LineVerticalCommand
        {
            get { return (ICommand)GetValue(LineVerticalCommandProperty); }
            private set { SetValue(LineVerticalCommandProperty, value); }
        }
        public static readonly DependencyProperty LineVerticalCommandProperty = DependencyProperty.Register("LineVerticalCommand", typeof(ICommand), typeof(ChartWithGraphicals), new PropertyMetadata(null));

        /// <summary>
        /// 通过数据模型添加已有图形
        /// </summary>
        /// <param name="model"></param>
        public void AddGraphicalFromModel(BaseGraphicalModel model)
        {
            if (model is SegmentXGraphicalModel sx) Graphicals.Add(new SegmentXGraphical(sx));
            else if (model is SegmentYGraphicalModel sy) Graphicals.Add(new SegmentYGraphical(sy));
            else if (model is RectangleGraphicalModel r) Graphicals.Add(new RectangleGraphical(r));
            else if (model is PolygonGraphicalModel p) Graphicals.Add(new PolygonGraphical(p));
            else if (model is QuadrantGraphicalModel q) Graphicals.Add(new QuadrantGraphical(q));
            else if (model is LineHorizontalGraphicalModel lh) Graphicals.Add(new LineHorizontalGraphical(lh));
            else if (model is LineVerticalGraphicalModel lv) Graphicals.Add(new LineVerticalGraphical(lv));
        }
        #endregion

        #region function
        internal override void ChangedNeedRedrawing()
        {
            base.ChangedNeedRedrawing();
            DrawingGraphicals();
        }
        /// <summary>
        /// 尺寸编号时重新绘制图形
        /// </summary>
        /// <param name="sizeInfo"></param>
        protected override void OnRenderSizeChanged(SizeChangedInfo sizeInfo)
        {
            base.OnRenderSizeChanged(sizeInfo);
            DrawingGraphicals();
        }
        /// <summary>
        /// 鼠标左键按下
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected override void Panel_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            var createingGraphical = Graphicals.FirstOrDefault(p => p.IsCreateing == true);
            if (createingGraphical != null)
            {
                createingGraphical.PanelMouseDown(sender, e);
                e.Handled = true;
                return;
            }
            base.Panel_MouseLeftButtonDown(sender, e);
        }
        /// <summary>
        /// 鼠标抬起
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected override void Panel_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            var createingGraphical = Graphicals.FirstOrDefault(p => p.IsCreateing == true || p.ControledShape != null);
            if (createingGraphical != null)
            {
                createingGraphical.PanelMouseUp(sender, e);
                e.Handled = true;
                return;
            }
            base.Panel_MouseLeftButtonUp(sender, e);
        }
        /// <summary>
        /// 鼠标移动
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        protected override void Panel_MouseMove(object sender, MouseEventArgs e)
        {
            var createingGraphical = Graphicals.FirstOrDefault(p => p.IsCreateing == true || p.ControledShape != null);
            if (createingGraphical != null)
            {
                createingGraphical.PanelMouseMove(sender, e);
                e.Handled = true;
                return;
            }
            base.Panel_MouseMove(sender, e);
        }
        /// <summary>
        /// 移动图形
        /// </summary>
        /// <param name="x"></param>
        /// <param name="y"></param>
        /// <param name="moveType"></param>
        internal override void Move(double x, double y, AxisChangeType moveType)
        {
            if (moveType == AxisChangeType.None) return;
            base.Move(x, y, moveType);
            DrawingGraphicals();
        }
        /// <summary>
        /// 缩放图形
        /// </summary>
        /// <param name="point"></param>
        /// <param name="percent"></param>
        /// <param name="zoomType"></param>
        internal override void Zoom(Point point, double percent, AxisChangeType zoomType)
        {
            if (zoomType == AxisChangeType.None) return;
            base.Zoom(point, percent, zoomType);
            DrawingGraphicals();
        }
        /// <summary>
        /// 绘制所有门
        /// </summary>
        protected virtual void DrawingGraphicals()
        {
            if (!IsLoaded) return;
            foreach (var graphical in Graphicals) graphical.Drawing();
        }
        /// <summary>
        /// 重新绘制图形
        /// </summary>
        /// <param name="NeedRefreshAreaSource"></param>
        protected override void DrawingSeries(bool NeedRefreshAreaSource = true)
        {
            base.DrawingSeries();
            if (NeedRefreshAreaSource && Graphicals != null)
            {
                foreach (var graphical in Graphicals)
                {
                    graphical.RefreshAreaSource(this.XValueConverter, this.YValueConverter);
                }
            }
        }
        #endregion

        #region 区域划分
        /// <summary>
        /// 区域名称显示的颜色
        /// </summary>
        public Brush AreaNameForeground
        {
            get { return (Brush)GetValue(AreaNameForegroundProperty); }
            set { SetValue(AreaNameForegroundProperty, value); }
        }
        public static readonly DependencyProperty AreaNameForegroundProperty = DependencyProperty.Register("AreaNameForeground", typeof(Brush), typeof(ChartWithGraphicals), new PropertyMetadata(Brushes.Black));
        /// <summary>
        /// 区域名称的字体大小
        /// </summary>
        public double AreaNameFontSize
        {
            get { return (double)GetValue(AreaNameFontSizeProperty); }
            set { SetValue(AreaNameFontSizeProperty, value); }
        }
        public static readonly DependencyProperty AreaNameFontSizeProperty = DependencyProperty.Register("AreaNameFontSize", typeof(double), typeof(ChartWithGraphicals), new PropertyMetadata(14d));

        /// <summary>
        /// 区域添加
        /// </summary>
        public ICommand GraphicalAreaAddCommand
        {
            get { return (ICommand)GetValue(GraphicalAreaAddCommandProperty); }
            set { SetValue(GraphicalAreaAddCommandProperty, value); }
        }

        public static readonly DependencyProperty GraphicalAreaAddCommandProperty = DependencyProperty.Register("GraphicalAreaAddCommand", typeof(ICommand), typeof(ChartWithGraphicals), new PropertyMetadata(null));

        /// <summary>
        /// 区域删除
        /// </summary>
        public ICommand GraphicalAreaRemoveCommand
        {
            get { return (ICommand)GetValue(GraphicalAreaRemoveCommandProperty); }
            set { SetValue(GraphicalAreaRemoveCommandProperty, value); }
        }

        public static readonly DependencyProperty GraphicalAreaRemoveCommandProperty = DependencyProperty.Register("GraphicalAreaRemoveCommand", typeof(ICommand), typeof(ChartWithGraphicals), new PropertyMetadata(null));

        /// <summary>
        /// 图中添加一个区域
        /// </summary>
        /// <param name="area"></param>
        internal void AddGraphicalArea(params GraphicalArea[] areas)
        {
            foreach (var area in areas)
            {
                if (area != null && area.DisplayElement == null)
                {
                    var tbx = new TextBlock() { HorizontalAlignment = HorizontalAlignment.Left, VerticalAlignment = VerticalAlignment.Top, Focusable = false };
                    tbx.SetBinding(TextBlock.MarginProperty, new Binding("Center") { Source = area, Mode = BindingMode.OneWay, Converter = Converters.TextBlockCenterPointToThicknessConverter.Converter, ConverterParameter = tbx });
                    tbx.SetBinding(TextBlock.TextProperty, new Binding("Name") { Source = area, Mode = BindingMode.OneWay });
                    tbx.SetBinding(TextBlock.ForegroundProperty, new Binding("AreaNameForeground") { Source = this, Mode = BindingMode.OneWay });
                    tbx.SetBinding(TextBlock.FontSizeProperty, new Binding("AreaNameFontSize") { Source = this, Mode = BindingMode.OneWay });
                    area.DisplayElement = tbx;
                    this.ViewPanel.Children.Add(tbx);
                    if (GraphicalAreaAddCommand != null && GraphicalAreaAddCommand.CanExecute(area)) GraphicalAreaAddCommand?.Execute(area);
                    if (AllAreas != null && !AllAreas.Contains(area)) AllAreas.Add(area);
                }
            }
        }
        /// <summary>
        /// 图中移除一个区域
        /// </summary>
        /// <param name="area"></param>
        internal void RemoveGraphicalArea(params GraphicalArea[] areas)
        {
            foreach (var area in areas)
            {
                if (area != null && this.ViewPanel.Children.Contains(area.DisplayElement))
                {
                    this.ViewPanel.Children.Remove(area.DisplayElement);
                    if (GraphicalAreaRemoveCommand != null && GraphicalAreaRemoveCommand.CanExecute(area)) GraphicalAreaRemoveCommand?.Execute(area);
                    if (AllAreas != null && AllAreas.Contains(area)) AllAreas.Remove(area);
                }
            }
        }

        /// <summary>
        /// 所有的区域，用于直方图和散点图颜色变更
        /// </summary>
        public ObservableCollection<GraphicalArea> AllAreas
        {
            get { return (ObservableCollection<GraphicalArea>)GetValue(AllAreasProperty); }
            set { SetValue(AllAreasProperty, value); }
        }
        public static readonly DependencyProperty AllAreasProperty = DependencyProperty.Register("AllAreas", typeof(ObservableCollection<GraphicalArea>), typeof(ChartWithGraphicals), new PropertyMetadata(AllAreasChanged));

        private static void AllAreasChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            if (d is ChartWithGraphicals chart)
            {
                if (e.NewValue != null && e.NewValue is ObservableCollection<GraphicalArea> areas)
                {
                    areas.CollectionChanged += chart.Areas_CollectionChanged;
                    foreach (var area in areas) area.PropertyChanged += chart.Area_PropertyChanged;
                }
                if (e.OldValue != null && e.OldValue is ObservableCollection<GraphicalArea> oldareas)
                {
                    oldareas.CollectionChanged -= chart.Areas_CollectionChanged;
                    foreach (var area in oldareas) area.PropertyChanged -= chart.Area_PropertyChanged;
                }
            }
        }

        private void Areas_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            if (this.Series is Series.HistogramSeries || this.Series is Series.ScatterSeries)
            {
                switch (e.Action)
                {
                    case NotifyCollectionChangedAction.Add:
                        foreach (GraphicalArea item in e.NewItems)
                        {
                            item.PropertyChanged += Area_PropertyChanged;
                        }
                        break;
                    case NotifyCollectionChangedAction.Remove:
                        foreach (GraphicalArea item in e.OldItems)
                        {
                            item.PropertyChanged -= Area_PropertyChanged;
                            if (this.Series != null) this.Series.RemoveArea(item);
                        }
                        break;
                    default:
                        break;
                }
            }
        }

        private void Area_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e)
        {
            if (sender is GraphicalArea area && this.Series != null)
            {
                if ("DisplayColor".Equals(e.PropertyName))
                {
                    this.Series.UpdateAreaDisplayColor(area);
                }
                else if ("InsideIndexs".Equals(e.PropertyName))
                {
                    this.Series.UpdateAreaIndexs(area, this.XSource, this.YSource, this.XAxis, this.YAxis, this.Indexs, this.XValueConverter, this.YValueConverter);
                }
            }
        }

        #endregion
    }
}
